package crmdna.client.group;

import static crmdna.common.OfyService.ofy;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import com.google.appengine.api.memcache.MemcacheServiceFactory;
import com.googlecode.objectify.Key;

import crmdna.client.member.Member;
import crmdna.client.member.MemberCore.MemberProp;
import crmdna.client.sequence.Sequence;
import crmdna.client.sequence.Sequence.SequenceType;
import crmdna.client.user.User;
import crmdna.client.user.User.Action;
import crmdna.client.user.User.ResourceType;
import crmdna.client.user.UserCore.UserProp;
import crmdna.common.APIException;
import crmdna.common.APIResponse.Status;
import crmdna.crmdna.Client;

public class Group {	
	public static class GroupProp {
		public long groupId;
		public String name;
		public String displayName;		
	}
	
	public static GroupProp create(String client, String displayName, String login) {
		
		Client.ensureValidClient(client);
		User.ensureAccess(client, login, ResourceType.GROUP, 0, Action.WRITE);
		
		String name = displayName.toLowerCase();
		
		List<Key<GroupEntity>> keys = ofy(client).load()
				.type(GroupEntity.class).filter("name", name).keys().list();
		
		if (keys.size() != 0)
			throw new APIException(Status.ERROR_RESOURCE_ALREADY_EXISTS, 
					"There is already a group with name [" + displayName + "]");
			
		String key = getUniqueKey(client, name);
		long val = MemcacheServiceFactory.getMemcacheService()
				.increment(key, 1, (long)0);
		
		if (val != 1)
			throw new APIException(Status.ERROR_RESOURCE_ALREADY_EXISTS, 
					"There is already a group with name [" + displayName + "]");
		
		GroupEntity entity = new GroupEntity();		
		entity.groupId = Sequence.getNext(client, SequenceType.GROUP);
		entity.name = name;
		entity.displayName = displayName;
		ofy(client).save().entity(entity).now();
		
		return entity.toProp();	
	}
	
	private static String getUniqueKey(String namespace, String name) {
		return namespace + "_" + SequenceType.GROUP + "_" + name;
 	}
	
	public static GroupEntity safeGet(String client, long groupId) {
		
		Client.ensureValidClient(client);
		
		GroupEntity entity = ofy(client).load().type(GroupEntity.class).id(groupId).get();
		if (null == entity)
			throw new APIException(Status.ERROR_RESOURCE_DOES_NOT_EXIST, 
					"Group [" + groupId + "] does not exist");
		return entity;		
	}		
	
	public static GroupProp rename(String client, long groupId, String newDisplayName, String login) {
		Client.ensureValidClient(client);
		User.ensureAccess(client, login, ResourceType.GROUP, 0, Action.WRITE);
		
		GroupEntity groupEntity = safeGet(client, groupId);
		
		String newName = newDisplayName.toLowerCase();
		
		if (groupEntity.name.equals(newName)) {
			//ideally should be inside a transaction
			groupEntity.displayName = newDisplayName;
			ofy(client).save().entity(groupEntity).now();
			return groupEntity.toProp();
		}
		
		List<Key<GroupEntity>> keys = ofy(client).load()
				.type(GroupEntity.class).filter("name", newName).keys().list();		
		if (keys.size() != 0)
			throw new APIException(Status.ERROR_RESOURCE_ALREADY_EXISTS, 
					"There is already a center with name [" + newDisplayName + "]");
				
		String key = getUniqueKey(client, newDisplayName);
		long val = MemcacheServiceFactory.getMemcacheService()
				.increment(key, 1, (long)0);
		
		if (val != 1)
			throw new APIException(Status.ERROR_RESOURCE_ALREADY_EXISTS, 
					"There is already a center with name [" + newDisplayName + "]");
		
		//ideally should be inside a transaction
		groupEntity.name = newName;
		groupEntity.displayName = newDisplayName;
		ofy(client).save().entity(groupEntity).now();
		
		return groupEntity.toProp();	
	}
	
	public static List<GroupProp> getAll(String client) {
		Client.ensureValidClient(client);
		
		List<GroupEntity> entities = ofy(client).load().type(GroupEntity.class).order("name").list();
		
		List<GroupProp> props = new ArrayList<>();
		for (GroupEntity entity : entities) 
			props.add(entity.toProp());
		
		return props;						
	}
	
	public static Set<Long> getAllGroupIds(String client) {		
		
		List<GroupProp> props = getAll(client);
		
		Set<Long> groupIds = new HashSet<>();
		for (GroupProp prop : props) 
			groupIds.add(prop.groupId);
		
		return groupIds;						
	}
	
	public static void ensureValidGroupIds(String client, List<Long> groupIds) {
		Set<Long> allGroupIds = Group.getAllGroupIds(client);
		for (int i = 0; i < groupIds.size(); i++) {
			long groupId = groupIds.get(i);
			if ((groupIds == null) || !allGroupIds.contains(groupId))
				throw new APIException(Status.ERROR_RESOURCE_DOES_NOT_EXIST, 
						"Error in record no [" + (i + 1) + 
						"]: Group id [" + groupIds.get(i) + "] does not exist");
		}
	}
	
	public static void delete(String client, long groupId, String login) {
						
		Client.ensureValidClient(client);
		User.ensureAccess(client, login, ResourceType.GROUP, 0, Action.WRITE);
		
		//there should not be any users for this center
		List<UserProp> userProps = User.getAllForGroup(client, groupId);
		if (userProps.size() != 0) 
			throw new APIException(Status.ERROR_PRECONDITION_FAILED, 
					"This center has [" + userProps.size() + "] users." + 
					"Please assign the users to a different center before deleting this center");
		
		//there should not be any members for this center
		List<MemberProp> memberProps = Member.getAllForGroup(client, groupId, login);
		if (memberProps.size() != 0) 
			throw new APIException(Status.ERROR_PRECONDITION_FAILED, 
					"This center has [" + memberProps.size() + "] members." + 
					"Please assign the members to a different center before deleting this center");
		
			
		throw new APIException(Status.ERROR_NOT_IMPLEMENTED, 
				"This functionality is not implemented yet");
		
		//GroupEntity groupEntity = safeGet(client, groupId);				
		
		//ofy(client).delete().entity(groupEntity).now();	
	}
}
